home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000
/
Ham Radio 2000.iso
/
ham2000
/
tcp_ip
/
rose_nos
/
lapb.c
< prev
next >
Wrap
Text File
|
1992-02-25
|
21KB
|
881 lines
/* Link Access Procedures Balanced (LAPB), the upper sublayer of
* AX.25 Level 2.
* Copyright 1991 Phil Karn, KA9Q
*
* K5JB ROSE Support added
*/
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ax25.h"
#include "lapb.h"
#include "ip.h"
#include "netrom.h"
#include "config.h"
#ifdef ROSE
#include "cmdparse.h"
/* ROSE neighbor table structure */
typedef struct {
char roscall[AXALEN]; /* call of neighbor rose in header fmt */
char ipcall[AXALEN]; /* other IP station's ax25 call */
} rosetab;
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#define ROSENUMCKTS 10 /* number of ROSE circuits permitted */
rosetab rose_table[ROSENUMCKTS];
int rosenbr_nr = 0;
int rose_enable = FALSE;
#endif /* ROSE */
static void handleit __ARGS((struct ax25_cb *axp,int pid,struct mbuf *bp));
static void procdata __ARGS((struct ax25_cb *axp,struct mbuf *bp));
static int ackours __ARGS((struct ax25_cb *axp,int16 n));
static void clr_ex __ARGS((struct ax25_cb *axp));
static void enq_resp __ARGS((struct ax25_cb *axp));
static void inv_rex __ARGS((struct ax25_cb *axp));
/* Process incoming frames */
int
lapb_input(axp,cmdrsp,bp)
struct ax25_cb *axp; /* Link control structure */
int cmdrsp; /* Command/response flag */
struct mbuf *bp; /* Rest of frame, starting with ctl */
{
int control;
int class; /* General class (I/S/U) of frame */
int16 type; /* Specific type (I/RR/RNR/etc) of frame */
char pf; /* extracted poll/final bit */
char poll = 0;
char final = 0;
int16 nr; /* ACK number of incoming frame */
int16 ns; /* Seq number of incoming frame */
int16 tmp;
if(bp == NULLBUF || axp == NULLAX25){
free_p(bp);
return -1;
}
/* Extract the various parts of the control field for easy use */
if((control = PULLCHAR(&bp)) == -1){
free_p(bp); /* Probably not necessary */
return -1;
}
type = ftype(control);
class = type & 0x3;
pf = control & PF;
/* Check for polls and finals */
if(pf){
switch(cmdrsp){
case LAPB_COMMAND:
poll = YES;
break;
case LAPB_RESPONSE:
final = YES;
break;
}
}
/* Extract sequence numbers, if present */
switch(class){
case I:
case I+2:
ns = (control >> 1) & MMASK;
case S: /* Note fall-thru */
nr = (control >> 5) & MMASK;
break;
}
/* This section follows the SDL diagrams by K3NA fairly closely */
switch(axp->state){
case LAPB_DISCONNECTED:
switch(type){
case SABM: /* Initialize or reset link */
sendctl(axp,LAPB_RESPONSE,UA|pf); /* Always accept */
clr_ex(axp);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED);/* Resets state counters */
axp->srt = Axirtt;
axp->mdev = 0;
set_timer(&axp->t1,2*axp->srt);
start_timer(&axp->t3);
start_timer(&axp->t4);
break;
case DISC: /* Always answer a DISC with a DM */
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
case DM: /* Ignore to avoid infinite loops */
break;
default: /* All others get DM */
if(poll)
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
}
if(axp->state == LAPB_DISCONNECTED){ /* we can close connection */
stop_timer(&axp->t1); /* waste all the timers */
stop_timer(&axp->t3);
stop_timer(&axp->t4);
free_q(&axp->txq); /* lose transmit queue */
del_ax25(axp); /* clean out the trash */
}
break;
case LAPB_SETUP:
switch(type){
case SABM: /* Simultaneous open */
sendctl(axp,LAPB_RESPONSE,UA|pf);
break;
case DISC:
sendctl(axp,LAPB_RESPONSE,DM|pf);
nr_derate(axp);
free_q(&axp->txq);
stop_timer(&axp->t1);
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case UA: /* Connection accepted */
/* Note: xmit queue not cleared */
stop_timer(&axp->t1);
start_timer(&axp->t3);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED);
start_timer(&axp->t4);
break;
case DM: /* Connection refused */
free_q(&axp->txq);
stop_timer(&axp->t1);
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
default: /* Respond with DM only to command polls */
if(poll)
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
}
break;
case LAPB_DISCPENDING:
switch(type){
case SABM:
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
case DISC:
sendctl(axp,LAPB_RESPONSE,UA|pf);
break;
case UA:
case DM:
stop_timer(&axp->t1);
lapbstate(axp,LAPB_DISCONNECTED);
break;
default: /* Respond with DM only to command polls */
if(poll)
sendctl(axp,LAPB_RESPONSE,DM|pf);
break;
}
break;
case LAPB_CONNECTED:
switch(type){
case SABM:
sendctl(axp,LAPB_RESPONSE,UA|pf);
clr_ex(axp);
free_q(&axp->txq);
stop_timer(&axp->t1);
start_timer(&axp->t3);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED); /* Purge queues */
break;
case DISC:
free_q(&axp->txq);
sendctl(axp,LAPB_RESPONSE,UA|pf);
stop_timer(&axp->t1);
stop_timer(&axp->t3);
axp->reason = LB_NORMAL;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case DM:
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case UA:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish */
break;
case FRMR:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish link */
break;
case RR:
case RNR:
axp->flags.remotebusy = (control == RNR) ? YES : NO;
if(poll)
enq_resp(axp);
ackours(axp,nr);
break;
case REJ:
axp->flags.remotebusy = NO;
if(poll)
enq_resp(axp);
ackours(axp,nr);
stop_timer(&axp->t1);
start_timer(&axp->t3);
/* This may or may not actually invoke transmission,
* depending on whether this REJ was caused by
* our losing his prior ACK.
*/
inv_rex(axp);
break;
case I:
ackours(axp,nr); /** == -1) */
start_timer(&axp->t4);
if(len_p(axp->rxq) >= axp->window){
/* Too bad he didn't listen to us; he'll
* have to resend the frame later. This
* drastic action is necessary to avoid
* deadlock.
*/
if(poll)
sendctl(axp,LAPB_RESPONSE,RNR|pf);
free_p(bp);
bp = NULLBUF;
break;
}
/* Reject or ignore I-frames with receive sequence number errors */
if(ns != axp->vr){
if(axp->proto == V1 || !axp->flags.rejsent){
axp->flags.rejsent = YES;
sendctl(axp,LAPB_RESPONSE,REJ | pf);
} else if(poll)
enq_resp(axp);
axp->response = 0;
break;
}
axp->flags.rejsent = NO;
axp->vr = (axp->vr+1) & MMASK;
tmp = len_p(axp->rxq) >= axp->window ? RNR : RR;
if(poll){
sendctl(axp,LAPB_RESPONSE,tmp|PF);
} else {
axp->response = tmp;
}
procdata(axp,bp);
bp = NULLBUF;
break;
default: /* All others ignored */
break;
}
break;
case LAPB_RECOVERY:
switch(type){
case SABM:
sendctl(axp,LAPB_RESPONSE,UA|pf);
clr_ex(axp);
stop_timer(&axp->t1);
start_timer(&axp->t3);
axp->unack = axp->vr = axp->vs = 0;
lapbstate(axp,LAPB_CONNECTED); /* Purge queues */
if(!run_timer(&axp->t4))
start_timer(&axp->t4);
break;
case DISC:
free_q(&axp->txq);
sendctl(axp,LAPB_RESPONSE,UA|pf);
stop_timer(&axp->t1);
stop_timer(&axp->t3);
axp->response = UA;
axp->reason = LB_NORMAL;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case DM:
nr_derate(axp);
axp->reason = LB_DM;
lapbstate(axp,LAPB_DISCONNECTED);
break;
case UA:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish */
break;
case FRMR:
est_link(axp);
lapbstate(axp,LAPB_SETUP); /* Re-establish link */
break;
case RR:
case RNR:
axp->flags.remotebusy = (control == RNR) ? YES : NO;
if(axp->proto == V1 || final){
stop_timer(&axp->t1);
ackours(axp,nr);
if(axp->unack != 0){
inv_rex(axp);
} else {
start_timer(&axp->t3);
lapbstate(axp,LAPB_CONNECTED);
if(!run_timer(&axp->t4))
start_timer(&axp->t4);
}
} else {
if(poll)
enq_resp(axp);
ackours(axp,nr);
/* Keep timer running even if all frames
* were acked, since we must see a Final
*/
if(!run_timer(&axp->t1))
start_timer(&axp->t1);
}
break;
case REJ:
axp->flags.remotebusy = NO;
/* Don't insist on a Final response from the old proto */
if(axp->proto == V1 || final){
stop_timer(&axp->t1);
ackours(axp,nr);
if(axp->unack != 0){
inv_rex(axp);
} else {
start_timer(&axp->t3);
lapbstate(axp,LAPB_CONNECTED);
if(!run_timer(&axp->t4))
start_timer(&axp->t4);
}
} else {
if(poll)
enq_resp(axp);
ackours(axp,nr);
if(axp->unack != 0){
/* This is certain to trigger output */
inv_rex(axp);
}
/* A REJ that acks everything but doesn't
* have the F bit set can cause a deadlock.
* So make sure the timer is running.
*/
if(!run_timer(&axp->t1))
start_timer(&axp->t1);
}
break;
case I:
ackours(axp,nr); /** == -1) */
/* Make sure timer is running, since an I frame
* cannot satisfy a poll
*/
if(!run_timer(&axp->t1))
start_timer(&axp->t1);
if(len_p(axp->rxq) >= axp->window){
/* Too bad he didn't listen to us; he'll
* have to resend the frame later. This
* drastic action is necessary to avoid
* memory deadlock.
*/
sendctl(axp,LAPB_RESPONSE,RNR | pf);
free_p(bp);
bp = NULLBUF;
break;
}
/* Reject or ignore I-frames with receive sequence number errors */
if(ns != axp->vr){
if(axp->proto == V1 || !axp->flags.rejsent){
axp->flags.rejsent = YES;
sendctl(axp,LAPB_RESPONSE,REJ | pf);
} else if(poll)
enq_resp(axp);
axp->response = 0;
break;
}
axp->flags.rejsent = NO;
axp->vr = (axp->vr+1) & MMASK;
tmp = len_p(axp->rxq) >= axp->window ? RNR : RR;
if(poll){
sendctl(axp,LAPB_RESPONSE,tmp|PF);
} else {
axp->response = tmp;
}
procdata(axp,bp);
bp = NULLBUF;
break;
default:
break; /* Ignored */
}
break;
}
free_p(bp); /* In case anything's left */
/* See if we can send some data, perhaps piggybacking an ack.
* If successful, lapb_output will clear axp->response.
*/
lapb_output(axp);
if(axp->response != 0){
sendctl(axp,LAPB_RESPONSE,axp->response);
axp->response = 0;
}
return 0;
}
/* Handle incoming acknowledgements for frames we've sent.
* Free frames being acknowledged.
* Return -1 to cause a frame reject if number is bad, 0 otherwise
*/
static int
ackours(axp,n)
struct ax25_cb *axp;
int16 n;
{
struct mbuf *bp;
int acked = 0; /* Count of frames acked by this ACK */
int16 oldest; /* Seq number of oldest unacked I-frame */
int32 rtt,abserr;
/* Free up acknowledged frames by purging frames from the I-frame
* transmit queue. Start at the remote end's last reported V(r)
* and keep going until we reach the new sequence number.
* If we try to free a null pointer,
* then we have a frame reject condition.
*/
oldest = (axp->vs - axp->unack) & MMASK;
while(axp->unack != 0 && oldest != n){
if((bp = dequeue(&axp->txq)) == NULLBUF){
/* Acking unsent frame */
return -1;
}
free_p(bp);
axp->unack--;
acked++;
if(axp->flags.rtt_run && axp->rtt_seq == oldest){
/* A frame being timed has been acked */
axp->flags.rtt_run = 0;
/* Update only if frame wasn't retransmitted */
if(!axp->flags.retrans){
rtt = msclock() - axp->rtt_time;
abserr = (rtt > axp->srt) ? rtt - axp->srt :
axp->srt - rtt;
/* Run SRT and mdev integrators */
axp->srt = ((axp->srt * 7) + rtt + 4) >> 3;
axp->mdev = ((axp->mdev*3) + abserr + 2) >> 2;
/* Update timeout */
set_timer(&axp->t1,4*axp->mdev+axp->srt);
}
}
axp->flags.retrans = 0;
axp->retries = 0;
oldest = (oldest + 1) & MMASK;
}
if(axp->unack == 0){
/* All frames acked, stop timeout */
stop_timer(&axp->t1);
start_timer(&axp->t3);
} else if(acked != 0) {
/* Partial ACK; restart timer */
start_timer(&axp->t1);
}
if(acked != 0){
/* If user has set a transmit upcall, indicate how many frames
* may be queued
*/
if(axp->t_upcall != NULLVFP)
(*axp->t_upcall)(axp,axp->paclen * (axp->maxframe - axp->unack));
}
return 0;
}
/* Establish data link */
void
est_link(axp)
struct ax25_cb *axp;
{
clr_ex(axp);
axp->retries = 0;
sendctl(axp,LAPB_COMMAND,SABM|PF);
stop_timer(&axp->t3);
start_timer(&axp->t1);
}
/* Clear exception conditions */
static void
clr_ex(axp)
struct ax25_cb *axp;
{
axp->flags.remotebusy = NO;
axp->flags.rejsent = NO;
axp->response = 0;
stop_timer(&axp->t3);
}
/* Enquiry response */
static void
enq_resp(axp)
struct ax25_cb *axp;
{
char ctl;
ctl = len_p(axp->rxq) >= axp->window ? RNR|PF : RR|PF;
sendctl(axp,LAPB_RESPONSE,ctl);
axp->response = 0;
stop_timer(&axp->t3);
}
/* Invoke retransmission */
static void
inv_rex(axp)
struct ax25_cb *axp;
{
axp->vs -= axp->unack;
axp->vs &= MMASK;
axp->unack = 0;
}
/* Send S or U frame to currently connected station */
int
sendctl(axp,cmdrsp,cmd)
struct ax25_cb *axp;
int cmdrsp;
int cmd;
{
if((ftype((char)cmd) & 0x3) == S) /* Insert V(R) if S frame */
cmd |= (axp->vr << 5);
return sendframe(axp,cmdrsp,cmd,NULLBUF);
}
/* Start data transmission on link, if possible
* Return number of frames sent
*/
int
lapb_output(axp)
register struct ax25_cb *axp;
{
register struct mbuf *bp;
struct mbuf *tbp;
char control;
int sent = 0;
int i;
if(axp == NULLAX25
|| (axp->state != LAPB_RECOVERY && axp->state != LAPB_CONNECTED)
|| axp->flags.remotebusy)
return 0;
/* Dig into the send queue for the first unsent frame */
bp = axp->txq;
for(i = 0; i < axp->unack; i++){
if(bp == NULLBUF)
break; /* Nothing to do */
bp = bp->anext;
}
/* Start at first unsent I-frame, stop when either the
* number of unacknowledged frames reaches the maxframe limit,
* or when there are no more frames to send
*/
while(bp != NULLBUF && axp->unack < axp->maxframe){
control = I | (axp->vs++ << 1) | (axp->vr << 5);
axp->vs &= MMASK;
dup_p(&tbp,bp,0,len_p(bp));
if(tbp == NULLBUF)
return sent; /* Probably out of memory */
sendframe(axp,LAPB_COMMAND,control,tbp);
start_timer(&axp->t4);
axp->unack++;
/* We're implicitly acking any data he's sent, so stop any
* delayed ack
*/
axp->response = 0;
if(!run_timer(&axp->t1)){
stop_timer(&axp->t3);
start_timer(&axp->t1);
}
sent++;
bp = bp->anext;
if(!axp->flags.rtt_run){
/* Start round trip timer */
axp->rtt_seq = (control >> 1) & MMASK;
axp->rtt_time = msclock();
axp->flags.rtt_run = 1;
}
}
return sent;
}
/* Set new link state */
void
lapbstate(axp,s)
struct ax25_cb *axp;
int s;
{
int oldstate;
oldstate = axp->state;
axp->state = s;
if(s == LAPB_DISCONNECTED){
stop_timer(&axp->t1);
stop_timer(&axp->t3);
stop_timer(&axp->t4);
free_q(&axp->txq);
}
/* Don't bother the client unless the state is really changing */
if((oldstate != s) && (axp->s_upcall != NULLVFP))
(*axp->s_upcall)(axp,oldstate,s);
}
/* Process a valid incoming I frame */
static void
procdata(axp,bp)
struct ax25_cb *axp;
struct mbuf *bp;
{
int pid;
int seq;
/* Extract level 3 PID */
if((pid = PULLCHAR(&bp)) == -1)
return; /* No PID */
if(axp->segremain != 0){
/* Reassembly in progress; continue */
seq = PULLCHAR(&bp);
if(pid == PID_SEGMENT
&& (seq & SEG_REM) == axp->segremain - 1){
/* Correct, in-order segment */
append(&axp->rxasm,bp);
if((axp->segremain = (seq & SEG_REM)) == 0){
/* Done; kick it upstairs */
bp = axp->rxasm;
axp->rxasm = NULLBUF;
pid = PULLCHAR(&bp);
handleit(axp,pid,bp);
}
} else {
/* Error! */
free_p(axp->rxasm);
axp->rxasm = NULLBUF;
axp->segremain = 0;
free_p(bp);
}
} else {
/* No reassembly in progress */
if(pid == PID_SEGMENT){
/* Start reassembly */
seq = PULLCHAR(&bp);
if(!(seq & SEG_FIRST)){
free_p(bp); /* not first seg - error! */
} else {
/* Put first segment on list */
axp->segremain = seq & SEG_REM;
axp->rxasm = bp;
}
} else {
/* Normal frame; send upstairs */
handleit(axp,pid,bp);
}
}
}
/* New-style frame segmenter. Returns queue of segmented fragments, or
* original packet if small enough
*/
struct mbuf *
segmenter(bp,ssize)
struct mbuf *bp; /* Complete packet */
int16 ssize; /* Max size of frame segments */
{
struct mbuf *result = NULLBUF;
struct mbuf *bptmp,*bp1;
int16 len,offset;
int segments;
/* See if packet is too small to segment. Note 1-byte grace factor
* so the PID will not cause segmentation of a 256-byte IP datagram.
*/
len = len_p(bp);
if(len <= ssize+1)
return bp; /* Too small to segment */
ssize -= 2; /* ssize now equal to data portion size */
segments = 1 + (len - 1) / ssize; /* # segments */
offset = 0;
while(segments != 0){
offset += dup_p(&bptmp,bp,offset,ssize);
if(bptmp == NULLBUF){
free_q(&result);
break;
}
/* Make room for segmentation header */
if((bp1 = pushdown(bptmp,2)) == NULLBUF){
free_p(bptmp);
free_q(&result);
break;
}
bp1->data[0] = PID_SEGMENT;
bp1->data[1] = --segments;
if(offset == ssize)
bp1->data[1] |= SEG_FIRST;
enqueue(&result,bp1);
}
free_p(bp);
return result;
}
static void
handleit(axp,pid,bp)
struct ax25_cb *axp;
int pid;
struct mbuf *bp;
{
struct axlink *ipp;
int check_rose __ARGS((struct ax25_cb *axp));
extern int rose_enable;
#ifdef ROSE
if (rose_enable && pid == PID_NO_L3 && check_rose(axp))
pid = PID_IP; /* replace PID removed by ROSE switch */
#endif
for(ipp = Axlink;ipp->funct != NULL;ipp++){
if(ipp->pid == pid)
break;
}
if(ipp->funct != NULL)
(*ipp->funct)(axp->iface,axp,NULLCHAR,NULLCHAR,bp,0);
else
free_p(bp);
}
#ifdef ROSE
/* test for rose connection via ax.25 */
int
check_rose(axp)
register struct ax25_cb *axp ;
{
register int i ;
int addreq();
int found = 0 ;
struct ax_route *axr;
if((axr = ax_lookup(axp->remote)) == NULLAXR)
return(FALSE); /* probably can't happen */
if(axr->ndigis < 2 || axr->ndigis > 4)
return(FALSE); /* can't be ROSE */
for (i = 0 ;i<rosenbr_nr; i++)
if (addreq(rose_table[i].ipcall,axp->remote)) {
found = 1;
break;
}
if(!found)
return(FALSE);
/* neighbor ROSE address will normally be first
* but sometimes second place */
if (addreq(rose_table[i].roscall,axr->digis[0]) ||
/* now try least likely case where a digi was used
* between rose and us
*/
addreq(rose_table[i].roscall,axr->digis[1]))
return(TRUE);
return(FALSE);
}
/*
* use: rose add IPcall Rosecall
*/
static int
rose_add(argc,argv)
int argc;
char *argv[];
{
if(rosenbr_nr == ROSENUMCKTS - 1){
tprintf("Rose inbound circuit table full!\n");
return(-1);
}
if(setcall(rose_table[rosenbr_nr].ipcall,argv[1]) == -1) {
tprintf("IP Call ?\n");
return(-1);
}
if(setcall(rose_table[rosenbr_nr].roscall,argv[2]) == -1) {
tprintf("ROSE Call?\n");
return(-1);
}
rosenbr_nr++;
rose_enable = TRUE; /* convenient global flag */
return(0);
}
static int
rose_drop(argc,argv)
int argc;
char *argv[];
{
int i;
char tmp[10];
if(setcall(tmp, argv[1]) == -1) {
printf("Call?\n");
return(-1);
}
for (i = 0; i < rosenbr_nr; i++)
if (addreq(rose_table[i].ipcall, tmp))
break;
if (i == rosenbr_nr)
return(-1); /* wasn't in table */
while (i<rosenbr_nr) {
memcpy(&rose_table[i].roscall,&rose_table[i+1].roscall,AXALEN);
memcpy(&rose_table[i].ipcall,&rose_table[i+1].ipcall,AXALEN);
i++;
}
rosenbr_nr--;
if (!rosenbr_nr)
rose_enable = FALSE;
return(0);
}
/* "rose" subcommands */
static struct cmds rosecmds[] = {
"add", rose_add, 0, 3,
"add failed, or syntax error: rose add <dest IP call> <neighbor rose>",
"drop", rose_drop, 0, 2,
"Not in table, or syntax error: rose drop <dest IP call>",
NULLCHAR, NULLFP, 0, 0,
"rose subcommands: add, drop",
NULLCHAR
};
/* Display and/or manipulate routing table */
int
dorose(argc,argv)
int argc;
char *argv[];
{
int i;
char tmp[10];
if (argc < 2) {
tprintf("Incoming Rose circuit table:\n");
if (!rosenbr_nr) {
tprintf("(empty)\n");
return(0);
}
for (i = 0; i < rosenbr_nr; i++) {
pax25(tmp,rose_table[i].ipcall);
tprintf("IP Call: %s, ", tmp);
pax25(tmp,rose_table[i].roscall);
tprintf("ROSE: %s\n", tmp);
}
return 0;
}
return subcmd(rosecmds,argc,argv,NULLCHAR);
}
#endif /* ROSE */